'''Visual Studio Driver'''
import os, sys, time
import win32con, win32process

class VsDriver:
    def __init__ (self, devenv_exe, devenv_sln):
        self.devenv_exe = devenv_exe
        self.devenv_sln = devenv_sln
        self._dte = None

    def start (self):
        cmd_line = '%s "%s"' % (self.devenv_exe, self.devenv_sln, )
        print 'xx cmd_line:', cmd_line
        flags = win32process.NORMAL_PRIORITY_CLASS
        si = win32process.STARTUPINFO()
        try:
            tpl_result = win32process.CreateProcess (None, cmd_line, None, None, 0, flags, None, '.', si)
        except:
            self.status_message ('COULD NOT START %s' % cmd_line)
            raise
        print 'xx tpl_result:', tpl_result
        self.devenv_proc = tpl_result[0]
        self.devenv_pid = tpl_result[2]
        print 'xx devenv_pid:', self.devenv_pid
        print 'xx self.dte.Solution.FullName:', self.dte.Solution.FullName

    def stop (self):
        win32process.TerminateProcess (self.devenv_proc, 0)

    def get_dte (self):
        if not self._dte:
            waitfor (self.get_dte_wrapper, extra=0.5)
        return self._dte
    dte = property (get_dte)

    def get_dte_wrapper (self):
        self._dte = get_dte_from_rot (pid=self.devenv_pid)
        return self._dte

    def open_file (self, filename, line_num=None, col_num=None):
        io = self.dte.ItemOperations
        rc = io.OpenFile (os.path.abspath (filename))
        sel = self.dte.ActiveDocument.Selection
        if line_num is not None and col_num is not None:
            sel.MoveToLineAndOffset (line_num, col_num)

    def get_file (self):
        doc = self.dte.ActiveDocument
        if not doc:
            return None
        pt = doc.Selection.ActivePoint
        file = os.path.join (doc.Path, doc.Name)
        return file

#----------------------------------------------------------------------

def get_devenv_exe (version=None):
    import win32api, win32con
    key = r'SOFTWARE\Classes\VisualStudio.DTE'
    if version:
        key += '.'+version
    key += r'\CLSID'
    hkey = win32api.RegOpenKey (win32con.HKEY_LOCAL_MACHINE, key)
    clsid = win32api.RegQueryValueEx (hkey, '')[0]
    # print 'xx clsid:', clsid
    hkey = win32api.RegOpenKey (win32con.HKEY_LOCAL_MACHINE,
            r'SOFTWARE\Classes\CLSID\%s\LocalServer32' % clsid)
    return win32api.RegQueryValueEx (hkey, '')[0]

def get_dte_from_rot (pid=None, sln=None):
    import pythoncom
    try:
        lst_dte = []
        rot = pythoncom.GetRunningObjectTable ()
        rot_enum = rot.EnumRunning()
        while 1:
            monikers = rot_enum.Next()
            if not monikers:
                break
            ctx = pythoncom.CreateBindCtx (0)
            display_name = monikers[0].GetDisplayName (ctx, None)
            # print 'xx display_name:', display_name
            if display_name.startswith ('!VisualStudio.DTE'):
                if pid and pid != get_pid_from_display_name(display_name):
                    continue
                obj = rot.GetObject (monikers[0])
                # print 'xx obj:', obj
                interface = obj.QueryInterface (pythoncom.IID_IDispatch)
                # print 'xx interface (IID_IDispatch):', interface
                import win32com.client
                dte = win32com.client.Dispatch (interface)
                # print 'xx dte.Solution.FullName:', dte.Solution.FullName
                if sln and not dte.Solution.FullName.endswith (sln):
                    continue
                lst_dte.append (dte)
        return lst_dte
    finally:
        rot = None

def get_pid_from_display_name (display_name):
    pid_pos = display_name.rfind (':')
    if pid_pos < 0:
        return None
    pid_str = display_name[pid_pos+1:]
    try: return int(pid_str)
    except ValueError: pass
    return None

def waitfor (condition, args=None, kwargs=None, seconds=10, interval=0.1, extra=None):
    if args is None: args = []
    if kwargs is None: kwargs = {}
    # print 'xx waitfor condition:', condition
    while seconds > 0:
        # print 'xx testing, seconds:', seconds
        if condition(*args, **kwargs):
            if extra:
                time.sleep (extra)
            return True
        time.sleep (interval)
        seconds -= interval
    return False

#----------------------------------------------------------------------

def main ():
    # print 'xx get_devenv_exe(7.1):', get_devenv_exe('7.1')
    # print 'xx get_devenv_exe(8.0):', get_devenv_exe('8.0')
    # print 'xx get_pid_from_display_name (abc:123):', get_pid_from_display_name('abc:123')
    # print 'xx get_pid_from_display_name (xyz):', get_pid_from_display_name('xyz')
    # dte = get_dte_from_rot (4780)
    # dte = get_dte_from_rot (sln='Vs7VimTestCs.sln')
    # dte = get_dte_from_rot ()
    # print 'xx dte.Solution.FullName:', dte.Solution.FullName

#     def waiter2 (lst):
#         print 'xx waiter2 lst:', lst
#         if not lst:
#             return True
#         lst.pop()
#         return False
#     waitfor (waiter2, 10, args=(range(3),), extra=5)

        vs_7 = get_devenv_exe('7.1')
        vs = VsDriver (vs_7, r'Vs7VimTestCs\Vs7VimTestCs.sln')
        vs.start ()
        vs.open_file (r'Vs7VimTestCs\Main\Main.cs')
        import pdb; pdb.set_trace ()
        vs.stop ()

if __name__ == '__main__':
    main ()
